/*
	PlAr is Platform Arena: a 2D multiplayer shooting game
	Copyright (c) 2010, Antonio Ragagnin <spocchio@gmail.com>
	All rights reserved.

	This file is licensed under the New BSD License.
*/

package plar.core;

import java.util.*;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.jbox2d.dynamics.World;
import org.jbox2d.collision.*;
import java.util.Collections;
import org.jbox2d.common.Vec2;
import plar.ClientServer.ShownElement;

/**
* Game is a sort of interface of the Level (that contains all of the Element)
* end the user that sends keyboard Input and receive the getScreen()
*
* It is used by the Server layer
*
* @author Antonio Ragagnin
*
*/
public class Game extends Thread {

	/**
* the state of the game, 1... ** t ocommment ** i have really no idea about the use of this variable.
*/
	private int gamestate;
	
	/**
	* point to the level
	*/
	public Level level;
	
	/**
	* the scale factor from the reality and the world to send to the client.
	*/
	public Vec2 scale;
	
	/**
	* chat between users
	*/
	private List<String> chat;
	
	/**
	* resolution of the screen of the client, in pixel.
	*/
	public Point screenResolution;
	/**
	* resolution of the piece of world show in the client, in meters.
	*/
	public Vec2 gameResolution;
	
	/**
	* list of aviable guns
	*/
	public List<String> gunList;
	
	/**
	* list of aviable players
	*/
	public List<String> playerList;
	
	/**
	* remaining time from the end of the game
	*/
	public long remaning=0;
	public Game() {
		screenResolution = new Point(800, 600);
		gameResolution = new Vec2(10f , 10f );
		chat = new ArrayList<String>();
		for (int i = 0; i < 10; i++)
		chat.add("");
		scale = new Vec2();
	}
	public synchronized  void restart()
	{
		List<ElementPlayer> ap= level.getPlayers();
		for(ElementPlayer p:ap)
		{
			p.kills=p.killed=0;
			level.delElement(p); 
			level.reSpawn(p);
		}

	}
	
	public synchronized  void setResolution(float x,float y)
	
	{
		gameResolution = new Vec2(x,y);
	}

	public synchronized void addPlayer(ElementPlayer s) {
		level.respawnQueue.add(s);
	}

	/**
	* return the screen centered on currentPlayer
	*/
	public synchronized ArrayList<ShownElement> getScreen(ElementPlayer currentPlayer) {
		if(currentPlayer==null) return new ArrayList<ShownElement>();
		Vec2 curpos = currentPlayer.getPosition();
		if(curpos==null) return new ArrayList<ShownElement>();
		Vec2 gameScreen = new Vec2();
		gameScreen.x = curpos.x - gameResolution.x / 2;
		gameScreen.y = curpos.y - gameResolution.y / 2;
		List insideL = level.getScreen(gameScreen, gameResolution);
		ArrayList<ShownElement> se = new ArrayList<ShownElement>();

		Collections.sort(insideL);
		ArrayList <Element> inside = (ArrayList <Element> ) insideL;
		for (Element e : inside) {
			Point p = new Point(
			(int) (scale.x * (e.getPosition().x - gameScreen.x)),
			(int) (scale.y * (e.getPosition().y - gameScreen.y)));

			SpriteSet ss = e.getCurrentSpriteSet();
			if (ss != null) {
				if (ss.autoSize)
				ss.updateSpriteSize(e);
				se.add(new ShownElement(p, ss, e.getAngle()));
			}
		}
		return se;

	}

	public  void spawn(ElementPlayer p) {
		sendChat(p.username + " respawn!");
		p.gl.clear();
		if (p.gl.size() == 0)
		for (String sgun : gunList) {
			Common.info(2, sgun);
			try {

				@SuppressWarnings("unchecked")
				Class c = Class.forName(Common.gunsPackage+sgun);

				Gun ogun = (Gun) c.newInstance();

				ogun.owner = p;
				p.gl.add(ogun);

			} catch (Exception e) {
				e.printStackTrace();
			} catch (java.lang.NoClassDefFoundError e) {
				e.printStackTrace();
			}
		}
		p.currentGunIndex = 0;

		ArrayList<Element> al = (ArrayList<Element>) level
		.getElementsByType(Common.ElementType.SPAWNPOINT);
		int rand = (int) (Math.random() * al.size());
		rand = rand % al.size();
		Element spawn = al.get(rand);
		p.setPosition(spawn.getPosition().x , spawn.getPosition().y);
		Common.PlayerSpawnInfo psi = new Common.PlayerSpawnInfo();
		psi.energy=new Integer(Common.getProperty("startingLife"));
		int startAmmoID=new Integer(Common.getProperty("initialAmmoID"));
		p.gl.get(startAmmoID).addAmmo(new Integer(Common.getProperty("startingAmmo")));
		psi.guns=p.gl;
		p.actions.forceAction("status", psi );
		level.addElement(p);
		

	}

	public synchronized void run() {

		if (gamestate != 1)
		return;

		// check who died and add it to the dead list

	//	Ok();
		try {
			float dt = level.Dt;// / level.fractionT;
			//Common.info(7, "Game.run() simulating "+dt+" x "+level.fractionT+ " = "+level.Dt);
			//for (int i = 0; i < level.fractionT; i++)
			level.world.step(dt, 1 ,1);
		} catch (Exception ex) {
			Common.info(7, "---> Game.run() JBox2D FAILED - world locked.");
			ex.printStackTrace();
		}
		for (Element e : level.destroyQueue) {
			// Common.info(3, "Game.delElement(): deleting "
			// + e.body.getUserData());

			// e.body.setUserData(null);
			level.world.destroyBody(e.body);

		}
		for (Element e : level.respawnQueue) {
			ElementPlayer p = (ElementPlayer) e;
			spawn(p);
		}
		level.respawnQueue.clear();
		level.destroyQueue.clear();
		level.run();
		for (Element e : level.getRunnable()) {
		//	Ok();
			e.run();
		}

	}

	public synchronized void addElement(Element e) {
		level.addElement(e);
	}
/*
	public synchronized boolean ok() {
		return !level.world.isLocked();
	}

	public synchronized  void Ok() {
		while (!ok()) {
			try {
				delta();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

	}
*/
	public synchronized void delta() throws InterruptedException {
		sleep(20);
	}

	public synchronized void sendInput(ElementPlayer p, KeyFlag premuti) {
		// Common.info(7,p.toString()+" sent "+premuti);
		//Ok();
		p.actions.forceAction("input", premuti);

	}

	public synchronized boolean setLevel(Level l) {

		l.world = new World(l.g, true);

		Common.info(1, "Game.setLevel() Loading level... of dimension" + l.dim + " and force"
		+ l.world.getGravity());
		l.Initialize();
		scale.x = ((float) (screenResolution.x)) / gameResolution.x;
		scale.y = ((float) (screenResolution.y)) / gameResolution.y;
		//level.world.setWarmStarting(true);
		//level.world.setContinuousPhysics(true);
		if (gamestate == 0)
		level = l;
		return true;
	}

	public synchronized void sendChat(ElementPlayer p, String s) {
		String nick;
		nick = p.username;

		sendChat("<"+nick+"> "+s);

	}

	public synchronized  void sendChat(String s) {
		Calendar cal = new GregorianCalendar();
		int ore = cal.get(+ Calendar.HOUR_OF_DAY);

		int minuti = cal.get(Calendar.MINUTE);
		int secondi = cal.get(Calendar.SECOND);

		chat.add(chat.size() - 1, "[" + ore + ":" + minuti +"] " + s);
		chat.remove(0);

	}

	public  String getGuns(ElementPlayer p) {
		String s="";
		for(int i=0; i<  gunList.size(); i++)
		{
			if(p.currentGunIndex==i) s+="["+gunList.get(i)+"]";
			else  s+=" "+gunList.get(i)+" ";
			s+=" "+p.gl.get(i).ammo+"\r\n";
		}
		return s;
	}
	public synchronized int getScore(ElementPlayer p) {
		return p.kills;
	}
	public synchronized int getSpawns(ElementPlayer p) {
		return p.killed;
	}
	public synchronized String getChat() {
		return Common.joinAL(chat);

	}

	public synchronized void startGame() {
		if (gamestate != 0)
		return;
		gamestate++;
	}

}
